package com.unwiredappeal.mediastreams;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.Arrays;
import java.util.List;

import com.unwiredappeal.mediastreams.mp4.MP4StreamFactory;
import com.unwiredappeal.mediastreams.mp4.MP4Streamer;
import com.unwiredappeal.tivo.config.ConfigEntry;
import com.unwiredappeal.tivo.config.StreamBabyConfig;
import com.unwiredappeal.tivo.utils.Log;
import com.unwiredappeal.tivo.utils.Utils;
import com.unwiredappeal.tivo.modules.BaseVideoHandlerModule;
import com.unwiredappeal.tivo.modules.StreamBabyModule;
import com.unwiredappeal.tivo.modules.VideoFormats;
import com.unwiredappeal.tivo.modules.VideoFormats.AllowableFormats;
import com.unwiredappeal.tivo.modules.VideoFormats.Format;

public class MP4StreamingModule extends BaseVideoHandlerModule implements StreamBabyModule {
	

	//public static AllowableFormats streamableFormats = new AllowableFormats(new Formats(new String[] { VideoHandlerModule.CONTAINER_MP4 }, new String[] { "*" }, new String[] { "*" }), null);
	public static AllowableFormats streamableFormats = new AllowableFormats(
			// allowed
			Arrays.asList(new Format[] { 
					new Format(VideoFormats.CONTAINER_MP4, VideoFormats.VIDEO_CODEC_H264, VideoFormats.AUDIO_CODEC_AC3),
					new Format(VideoFormats.CONTAINER_MP4, VideoFormats.VIDEO_CODEC_H264, VideoFormats.AUDIO_CODEC_AAC)
			}),
			// disallowed
			null
		);	
	
	public static String[] mp4Exts = new String[] { "mp4" };
	
	public static ConfigEntry cfgFillVideoInfo = new ConfigEntry(
			"mp4mod.fillvidinfo",
			"true",
			"Allow mp4module to parse video informaton"
			);
	
	
	public static ConfigEntry cfgStreamableFormats = new ConfigEntry(
			"mp4mod.streamformats",
			"default",
			"list of formats mp4module should attempt to stream"
			);
	
	public static ConfigEntry cfgNotStreamableFormats = new ConfigEntry(
			"mp4mod.streamformats.disallow",
			"default",
			"list of formats mp4module should not attempt to stream"
			);

	@Override
	public AllowableFormats getStreamableFormats() {
		return streamableFormats;
	}
	
	@Override
	public boolean fillVideoInformation(URI uri, VideoInformation vidinfo) {
		if (!cfgFillVideoInfo.getBool())
			return false;
		if (!Utils.isFile(uri))
			return false;
		String filename = new File(uri).getAbsolutePath();
		int dotPos = filename.lastIndexOf('.');
		if (dotPos < 0)
			return false;
		String ext = filename.substring(dotPos+1).toLowerCase();
		if (!Arrays.asList(mp4Exts).contains(ext))
			return false;
		try {
			MP4Streamer mp4 = MP4StreamFactory.getInstance(new File(uri), Long.MAX_VALUE, false);
			if (mp4.getSubDuration() <= 0)
				return false;
			vidinfo.setWidth(mp4.getWidth());
			vidinfo.setHeight(mp4.getHeight());
			vidinfo.setContainerFormat(VideoFormats.CONTAINER_MP4);
			vidinfo.setDuration(mp4.getSubDuration());
			List<String> formats = mp4.getFormats();
			if (formats.contains("avc1"))
				vidinfo.setVideoCodec(VideoFormats.VIDEO_CODEC_H264);
			else
				vidinfo.setVideoCodec(VideoFormats.UNKNOWN_FORMAT);
			if (formats.contains("mp4a"))
				vidinfo.setAudioCodec(VideoFormats.AUDIO_CODEC_AAC);
			else if (formats.contains("ac-3"))
				vidinfo.setAudioCodec(VideoFormats.AUDIO_CODEC_AC3);

			else
				vidinfo.setAudioCodec(VideoFormats.UNKNOWN_FORMAT);

			Log.debug(filename + ":" + vidinfo);
			mp4.close();
			return true;
		} catch (IOException e) {
			return false;
		}
		
	}

	@Override
	public VideoInputStream openStreamableVideo(URI uri, VideoInformation vidinfo, long startPosition)
			throws IOException {
		if (!Utils.isFile(uri))
			return null;

		MP4Streamer mis = MP4StreamFactory.getInstance(new File(uri), startPosition, StreamBabyConfig.cfgMp4Interleave.getBool());
		long subDur = mis.getSubDuration();
		if (subDur <= 0)
			subDur = vidinfo.getDuration();
		return new VideoInputStreamWrapper(subDur, mis, vidinfo, "video/mp4");
	}
	public Object getModule(int moduleType) {
		if (moduleType == StreamBabyModule.STREAMBABY_MODULE_VIDEO)
			return this;
		else
			return null;
	}

	public static final int FILL_VIDEO_PRIORITY = 35;
	public static final int STREAM_VIDEO_PRIORITY = 60;

	@Override
	public boolean initialize(StreamBabyModule parentMod) {
		super.initialize(parentMod);
		MP4StreamFactory.initialize();
		getPriorities().fillVideoPriority = FILL_VIDEO_PRIORITY;
		getPriorities().streamPriority = STREAM_VIDEO_PRIORITY;		
		streamableFormats = configFormats(cfgStreamableFormats, cfgNotStreamableFormats, streamableFormats);
		//if (!cfgStreamableFormats.getValue().equals("default") || !cfgNotStreamableFormats.getValue().equals("default"))
			//streamableFormats = new AllowableFormats(cfgStreamableFormats.getValue(), cfgNotStreamableFormats.getValue());
		
		
		return true;
	}
	public boolean isProfileOk(int profile, int level) {
		return (profile <= 100 && level <= 41);
	}
	public boolean canStream(URI uri, VideoInformation vinfo) {
		boolean b = super.canStream(uri, vinfo);
		if (!b)
			return b;
		
		
		// Ok, so far so good.
		Integer profile = (Integer)vinfo.getCodecExtra("mp4_profile");		
		Integer level = (Integer)vinfo.getCodecExtra("mp4_level");
		if (level != null && profile != null) {
			return isProfileOk(profile.intValue(), level.intValue());
		}
		
		try {
			MP4Streamer mp4 = MP4StreamFactory.getInstance(new File(uri).getAbsoluteFile(), Long.MAX_VALUE, false);
			mp4.close();
			vinfo.setCodecExtra("mp4_profile", new Integer(mp4.getProfile()));
			vinfo.setCodecExtra("mp4_level", new Integer(mp4.getProfileLevel()));			
			return isProfileOk(mp4.getProfile(), mp4.getProfileLevel());
		} catch (IOException e) {
			return false;
		}
	}

	
	public static void main(String[] argv) throws Exception {
		String src = argv[0];
		String dst = argv[1];
		long pos = Long.parseLong(argv[2]);
		System.err.println("In: " + src + ", out: " + dst + ", pos: " + pos);
		MP4Streamer mp4 = MP4StreamFactory.getInstance(new File(src).getAbsoluteFile(), pos, StreamBabyConfig.cfgMp4Interleave.getBool());
		System.err.println("SubDur: " + mp4.getSubDuration()/1000.0);
		OutputStream os = new FileOutputStream(new File(dst));
		final int IO_BUFFER_SIZE = 4 * 1024;

		byte[] b = new byte[IO_BUFFER_SIZE];
		int read;
		while ((read = mp4.read(b)) != -1) {
			os.write(b, 0, read);
		}
		os.close();
		mp4.close();
		System.out.println("Done");
	}

	public String getStreamableMimeType(URI uri, VideoInformation vinfo) {
		return "video/mp4";
	}

	public String getTranscodeMimeType(URI uri, VideoInformation vinfo, int qual) {
		return null;
	}



}
